package cn.mq;

import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.CMQCFC;
import com.ibm.mq.pcf.PCFMessage;
import com.ibm.mq.pcf.PCFMessageAgent;
import com.ibm.msg.client.wmq.v6.base.internal.MQC;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MQUtil {
    /**
     * 用于从配置映射中读取 MQ 相关的配置参数，设置 MQ 环境，并创建和返回一个 MQQueueManager 实例
     * @param mqConfigMap
     * @return
     */
    public static MQQueueManager getMQQueueMgr(Map mqConfigMap){
        if(null!=mqConfigMap && mqConfigMap.size()>0 && !mqConfigMap.isEmpty()){
            MQQueueManager queueManager = null;
            String MQ_Host = null;
            String MQ_Port = null;
            String MQ_Channel = null;
            String MQ_CCSID = null;
            String MQ_Manager = null;

            MQ_Host = null!=mqConfigMap.get("MQ_HOSTNAME")?mqConfigMap.get("MQ_HOSTNAME").toString().trim():"";
            MQ_Port = null!=mqConfigMap.get("MQ_PROT")?mqConfigMap.get("MQ_PROT").toString().trim():"";
            MQ_Channel = null!=mqConfigMap.get("MQ_CHANNEL")?mqConfigMap.get("MQ_CHANNEL").toString().trim():"";
            MQ_CCSID = null!=mqConfigMap.get("MQ_CCSID")?mqConfigMap.get("MQ_CCSID").toString().trim():"";
            MQ_Manager = null!=mqConfigMap.get("MQ_MANAGER")?mqConfigMap.get("MQ_MANAGER").toString().trim():"";

            MQEnvironment.hostname  = MQ_Host;
            MQEnvironment.channel = MQ_Channel;
            MQEnvironment.port =new Integer((String)MQ_Port);
            MQEnvironment.CCSID =new Integer((String) MQ_CCSID);
            MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES);

            try {
                queueManager = new MQQueueManager(MQ_Manager);
            } catch (MQException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if(null!=queueManager ){
                return queueManager;
            }
        }
        return null;
    }

    /**
     * 将消息发送到指定的 MQ 队列，包括设置消息的格式、字符集和自定义属性，并处理消息的提交和异常情况。
     * @param msg
     * @param queueName
     * @return
     */
    public static boolean sendMessageToMQ(String msg,
                                          String queueName) {
        // 调用来获取一个 MQQueueManager 对象，这个对象用于与 MQ 系统进行交互。
        MQQueueManager mqQueueManager = getMQQueueMgr(getPropertyContext());
        MQQueue queue = null;

        // 指定打开队列的选项
        // 指定队列用于输出消息
        // 如果队列管理器处于安静状态（即不接受连接），则操作失败。
        int openOptions = MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING;
        try {
            // 打开指定的队列
            queue = mqQueueManager.accessQueue(queueName.trim(), openOptions, null, null,
                    null);
            // MQPutMessageOptions 对象 pmo 用于设置消息放置的选项（在这里没有特别的设置）
            MQPutMessageOptions pmo = new MQPutMessageOptions();
            // MQMessage 对象 outMsg 用于封装待发送的消息。
            MQMessage outMsg = new MQMessage();

            // 设置消息格式为字符串。
            outMsg.format = MQC.MQFMT_STRING;
            // 设置消息的字符集为 UTF-8。
            outMsg.characterSet = 1208;

            outMsg.setStringProperty("requestId", UUID.randomUUID().toString());// 设置消息的自定义属性 requestId，值为随机生成的 UUID。
            outMsg.setStringProperty("serviceName", "SXXXXXXXXB");// 设置消息的自定义属性 serviceName，这是一个示例值，可能代表服务的名称或标识。
            outMsg.setStringProperty("sendTime", new Date().getTime() + "");// 设置消息的自定义属性 sendTime，值为当前时间的时间戳。
            outMsg.writeString(msg); //将传入的消息内容写入 outMsg 对象。
            queue.put(outMsg, pmo); //将消息放入队列中。
            mqQueueManager.commit();//提交事务，使消息的放置操作生效。


            return true;
        } catch (MQException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            // 关闭队列和队列管理器，以释放资源。
            closeMQQueueManager(queue, mqQueueManager);
        }
        return false;
    }

    /**
     * 使用消息浏览获取数据
     * @param queueName
     * @param targetMessageId
     * @return
     */
    public static String browseMessageFromQueue(String queueName, String targetMessageId) {
        String msg = null;
        MQQueueManager mqQueueManager = null;
        MQQueue queue = null;
        try {
            // 连接到消息队列管理器 (MQQueueManager)。
            mqQueueManager = getMQQueueMgr(getPropertyContext());
            int openOptions = MQC.MQOO_BROWSE | MQC.MQOO_FAIL_IF_QUIESCING;

            // 打开指定名称的队列 (queueName) 以浏览消息。
            queue = mqQueueManager.accessQueue(queueName.trim(), openOptions, null, null, null);

            // 设置消息浏览选项。
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            gmo.options = MQC.MQGMO_BROWSE_FIRST;  // 从队列的第一条消息开始浏览
            MQMessage message = new MQMessage();

            // 浏览消息
            while (true) {
                try {
                    queue.get(message, gmo);
                    if (targetMessageId.equals(message.messageId)) {
                        msg = message.readStringOfByteLength(message.getDataLength());
                        break;
                    }
                    gmo.options = MQC.MQGMO_BROWSE_NEXT;  // 获取下一条消息
                } catch (MQException e) {
                    if (e.getReason() == MQException.MQRC_NO_MSG_AVAILABLE) {
                        // 没有更多消息可浏览
                        break;
                    }
                    throw e;
                }
            }
        } catch (MQException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeMQQueueManager(queue, mqQueueManager);
        }
        return msg;
    }

    /**
     * 从队列中获取消息并返回其内容
     * @param queueName
     * @return
     */
    public static String receiverMessageToMQ(String queueName){
        String msg=null;
        MQQueueManager mqQueueManager = null;
        MQQueue queue = null;
        try {
            // 连接到消息队列管理器 (MQQueueManager)。
            mqQueueManager = getMQQueueMgr(getPropertyContext());
            int openOptions = MQC.MQOO_INPUT_SHARED
                    | MQC.MQOO_FAIL_IF_QUIESCING;

            // 打开指定名称的队列 (queueName) 以读取消息。
            queue = mqQueueManager.accessQueue(queueName.trim(), openOptions, null, null,
                    null);

            // 读取队列中的一条消息到 MQMessage 对象。
            MQMessage  theMessage     = new MQMessage();
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            queue.get(theMessage,gmo);

            //将消息内容转换为字符串 (msg)。
            msg=theMessage.readStringOfByteLength(theMessage.getDataLength());
        } catch (MQException e) {
            // TODO Auto-generated catch block
            //      如果发生 MQException 或 IOException，则处理异常，并在特定异常情况下返回 "000000"。
            e.printStackTrace();
            if(e.getReason() == 2033){
                return "000000";
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            closeMQQueueManager(queue, mqQueueManager);
        }
        return msg;
    }

    /**
     * 从 IBM MQ 队列管理器获取所有本地队列的列表。它使用了 IBM MQ 的 PCF（Programmable Command Formats）来执行队列查询。
     * @return 队列名称的列表。
     */
    public static List<String> getQueueList(){

        // 初始化 MQ 连接：
        MQQueueManager mqQueueManager = null; //mqQueueManager：表示 MQ 队列管理器的实例。
        PCFMessageAgent agent = null; //agent：表示 PCF 消息代理，用于发送 PCF 消息到队列管理器。
        try {
            // 获取队列管理器实例。
            mqQueueManager = getMQQueueMgr(getPropertyContext());
            // 创建一个 PCF 消息代理。
            agent = new PCFMessageAgent(mqQueueManager);
            // 设置字符集为 UTF-8
            agent.setCharacterSet(1208);
            // 创建并发送 PCF 消息：
            PCFMessage request = new PCFMessage(CMQCFC.MQCMD_INQUIRE_Q_NAMES); //创建一个 PCF 消息，命令是查询队列名称。
            request.addParameter(CMQC.MQCA_Q_NAME, "*"); //指定查询所有队列（* 表示通配符）。
            request.addParameter(CMQC.MQIA_Q_TYPE, MQC.MQQT_LOCAL); //指定只查询本地队列。
            PCFMessage[] responses = agent.send(request); //发送请求并接收响应。

            // 处理响应：
            String[] queueNameArray = (String[]) responses[0].getParameterValue(CMQCFC.MQCACF_Q_NAMES); //从响应中获取队列名称数组。
            if(null!=queueNameArray && queueNameArray.length>0){
                // 如果数组不为空，则调用 filterSystemMQ(queueNameArray) 处理并返回结果。
                return filterSystemMQ(queueNameArray);
            }
        } catch (MQException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();


        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            // 关闭资源：
            try {
                if (agent != null)
                    agent.disconnect();
                if (mqQueueManager != null)
                    mqQueueManager.disconnect();
            } catch (MQException e) {
                e.printStackTrace();
            }
        }
        // 如果没有找到队列或发生异常，返回 null。
        return null;
    }


    /**
     * 从一个包含队列名称的数组中筛选出所有用户定义的队列，过滤掉系统队列
     * 它首先创建一个新的列表 queueList，然后遍历输入的队列名称数组。
     * 对于每个队列名称，调用 isSystemQueue 方法检查是否为系统队列，若不是，则将其添加到 queueList 中。
     * 最后返回过滤后的队列名称列表。如果没有符合条件的队列，则返回 null。
     * @param queueNameArray 方法的输入参数，是一个包含队列名称的数组。
     * @return 过滤后的队列名称列表。
     */
    private static List<String> filterSystemMQ(String[] queueNameArray){
        List<String> queueList =  new ArrayList<String>();
        int arrayNum = queueNameArray.length;
        System.out.println("���� < "+arrayNum+" > ��");
        // 遍历 queueNameArray 数组的每个元素。
        for(int index = 0; index < arrayNum; index++ ){
            // 调用 isSystemQueue(queueNameArray[index]) 方法检查当前队列是否是系统队列。
            if(!isSystemQueue(queueNameArray[index])){
                // 如果当前队列不是系统队列，则将其添加到 queueList 中。
                queueList.add(queueNameArray[index]);
            }
        }
        // 检查 queueList 是否非空且包含元素。
        //如果 queueList 非空，返回 queueList。
        //如果 queueList 为空，则返回 null。
        if(null!=queueList && queueList.size() > 0){
            return queueList;
        }
        return null;
    }

    /**
     * 检查一个给定的队列名称是否属于系统队列。
     * @param qName 方法的输入参数，是一个字符串，表示队列的名称。
     * @return
     */
    private static boolean isSystemQueue(String qName) {
        // 检查队列名称 qName 是否为空（null）或者为空字符串（""）。
        // 如果是，则返回 false，表示这个队列名称不可能是系统队列。
        if (qName == null || qName.equals("")) {
            return false;
        }
        // 使用正则表达式 "(SYSTEM.)|(AMQ.)" 创建一个 Pattern 对象。这个正则表达式用于匹配包含 "SYSTEM." 或 "AMQ." 的队列名称。
        Matcher mc = Pattern.compile("(SYSTEM.)|(AMQ.)").matcher(qName);
        // 检查是否找到与正则表达式匹配的子字符串。
        if (mc.find()) {
            // 如果找到了匹配项，则返回 true，表示队列名称包含 "SYSTEM." 或 "AMQ."，因此被认为是系统队列。
            return true;
        }
        return false;
    }

    /**
     * 关闭和断开与消息队列相关的资源
     * @param queue
     * @param mqManager
     */
    public static void closeMQQueueManager(MQQueue queue, MQQueueManager mqManager) {
        try {
            if (queue != null)
                queue.close();
        } catch (MQException e) {
            e.printStackTrace();
        }
        try {
            if (mqManager != null)
                mqManager.disconnect();
        } catch (MQException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从 queueConfig.properties 配置文件中读取属性，并将这些属性存储到一个 Map 中，然后返回这个 Map
     * @return
     */
    public static Map<String,String> getPropertyContext(){
        Properties property = new Properties();
        InputStream ins2 = MQUtil.class.getClassLoader().getResourceAsStream("queueConfig.properties");
        try {
            property.load(ins2);
            String str_Host = property.getProperty("MQ_HOSTNAME").trim();
            String str_CHANNEL = property.getProperty("MQ_CHANNEL").trim();

            Map<String,String> map = new HashMap<String, String>();
            map.put("MQ_HOSTNAME", str_Host.trim());
            map.put("MQ_CHANNEL", str_CHANNEL.trim());
            map.put("MQ_PROT", property.getProperty("MQ_PROT").trim());
            map.put("MQ_CCSID", property.getProperty("MQ_CCSID").trim());
            map.put("MQ_MANAGER", property.getProperty("MQ_MANAGER").trim());
            ins2.close();

            if(null!=map&&!map.isEmpty()){
                return map;
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}
